package it.gnocco.state;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;

import it.gnocco.grafics.Finestrella;
import it.gnocco.grafo.*;
import it.gnocco.log.Logger;
import it.gnocco.robotica.*;

public class Stato implements Comparable<Stato> {

	public HashSet<Nodo> visitati = new HashSet<Nodo>();
	public ArrayList<Robot> robot = new ArrayList<Robot>();
	public HashSet<Nodo> raggiungibili = new HashSet<Nodo>();
	public double value = -1.0;
	public double k = 1.0;
	public Finestrella f = null;
	public ArrayList<Stato> possibili = new ArrayList<Stato>();
	public static int numero_nodi = 0;
	public static String CLASS = "Stato";
	public int index = -1;

	
	public Stato(HashSet<Nodo> nodi, ArrayList<Robot> robots, Finestrella f) {
		robot = robots;
		raggiungibili = new HashSet<Nodo>();
		visitati = nodi;
		this.f = f;
		for (Nodo n : visitati)
			for (int i = 0; i < n.sizeVicini(); i++)
				raggiungibili.add(n.getNodo(i));
	}

	public Stato(HashSet<Nodo> nodi, ArrayList<Robot> robots,
			HashSet<Nodo> visibili) {
		visitati = nodi;
		robot = robots;
		raggiungibili = visibili;
		for (Nodo n : visitati)
			for (int i = 0; i < n.sizeVicini(); i++)
				raggiungibili.add(n.getNodo(i));
	}

	public Stato(Finestrella window, HashSet<Nodo> nodi,
			ArrayList<Robot> robots, HashSet<Nodo> visibili) {
		visitati = nodi;
		robot = robots;
		raggiungibili = visibili;
		f = window;
		for (Nodo n : visitati)
			for (int i = 0; i < n.sizeVicini(); i++)
				raggiungibili.add(n.getNodo(i));
	}

	public Stato(Finestrella window, HashSet<Nodo> nodi,
			ArrayList<Robot> robots, HashSet<Nodo> visibili, int tempo) {
		visitati = nodi;
		robot = robots;
		raggiungibili = visibili;
		f = window;
		for (Nodo n : visitati)
			for (int i = 0; i < n.sizeVicini(); i++)
				raggiungibili.add(n.getNodo(i));
	}

	public void grafica(int nodi) {
		numero_nodi = nodi;
		String METHOD = "grafica";
		for (int i = 0; i < nodi; i++)
			f.aggiornaNodo(i, Color.white);
		for (Nodo n : raggiungibili) {
			f.aggiornaNodo(n.getID(), Color.yellow);
		}
		for (Nodo n : visitati) {
			f.aggiornaNodo(n.getID(), Color.green);
		}
		String Titolo = "Multi Robot Exploration ";
		for (Robot r : robot) {
			f.aggiornaNodo(r.getPosizione().getID(), r.colore);
			Titolo += "R:"+r.getId() +" N:"+r.getPosizione().getID()+", ";
		}
		Logger.log(Logger.info, CLASS, METHOD, "Titolo = " + Titolo);
		f.cambiaTitolo(Titolo);
	}

	public boolean equals(Stato s) {
		String METHOD = "equals";
		boolean result = false;
		for (Nodo d : s.visitati) {
			result = false;
			for (Nodo dd : this.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Nodo d : this.visitati) {
			result = false;
			for (Nodo dd : s.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Robot r : s.robot) {
			result = false;
			for (Robot rr : this.robot) {
				if (r.equals(rr) && r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;

		}
		for (Robot r : this.robot) {
			result = false;
			for (Robot rr : s.robot) {
				if (r.equals(rr) && r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;
		}
		if(this.index == -1)
			this.index = s.index;
		if(s.index == -1)
			s.index = this.index;
		return true;
	}

	public boolean equal(Stato s) {
		String METHOD = "equal";
		boolean result = false;
		for (Nodo d : s.visitati) {
			result = false;
			for (Nodo dd : this.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Nodo d : this.visitati) {
			result = false;
			for (Nodo dd : s.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Robot r : s.robot) {
			result = false;
			for (Robot rr : this.robot) {
				if (r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;

		}
		for (Robot r : this.robot) {
			result = false;
			for (Robot rr : s.robot) {
				if (r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;
		}
		return true;
	}

	public boolean equals(Object o) {
		String METHOD = "equals";
		if (!(this instanceof Stato))
			return false;
		Stato s = (Stato) o;
		boolean result = false;
		for (Nodo d : s.visitati) {
			result = false;
			for (Nodo dd : this.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Nodo d : this.visitati) {
			result = false;
			for (Nodo dd : s.visitati) {
				if (d.equals(dd))
					result = true;
			}
			if (result == false)
				return result;
		}
		for (Robot r : s.robot) {
			result = false;
			for (Robot rr : this.robot) {
				if (r.equals(rr) && r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;

		}
		for (Robot r : this.robot) {
			result = false;
			for (Robot rr : s.robot) {
				if (r.equals(rr) && r.getPosizione().equals(rr.getPosizione())) {
					result = true;
				}
			}
			if (result == false)
				return result;
		}
		if(this.index == -1)
			this.index = s.index;
		if(s.index == -1)
			s.index = this.index;
		return true;
	}

	public int hashCode() {
		double result = 1;
		Iterator<Nodo> iter = this.visitati.iterator();
		while (iter.hasNext()) {
			result *= Math.pow(2.0, (double) iter.next().getID());
		}
		return (int) result;
	}

	@Override
	public int compareTo(Stato o) {
		if (o == null){
			return 1;
		}
		if (this.value > o.value)
			return 1;
		if (this.value < o.value)
			return -1;
		return 0;
	}

	public String toString() {
		String result = "valore: " + value + ", ";
		result += "  Robot: ";
		for (Robot r : this.robot) {
			result += r.toString() + " -> " + r.getPosizione().getID() + ", ";
		}
		result += "  Visitati: ";
		for (Nodo n : visitati)
			result += n.toString() + ", ";
		return result;
	}

	public Stato getRandom(int tipo_scelta, int t, int volte){
		String METHOD = "getRandom"; 
		//ArrayList<Stato> appoggio = new ArrayList<Stato>();
		Stato fine = null;
		if(tipo_scelta==1){
			double val = -1;
			Stato stato = null;
			for(Stato s : possibili){
				if(s.visitati.size() == numero_nodi){
					Logger.log(Logger.message, CLASS, METHOD, "Finisco con lo stato " + s.toString());
					return s;
				}
				int l = s.visitati.size() - this.visitati.size();
				double appo = new Random().nextDouble();
				if(val<(appo*appo) * (Math.pow((double)(t/500.0) + 1.0,((s.value)/50.0 +1.0)) * (l*volte + 1))){
			//		Logger.log(Logger.message, CLASS, METHOD, "p = " + (appo*appo) * ((((double)t/200.0) + 1 + (l*volte)) * ((s.value)/25 +1)) + " per lo stato " + s.toString());
					val = (appo*appo) * (Math.pow((double)(t/500.0) + 1.0,((s.value)/50.0 +1.0)) * (l*volte + 1));
					stato = s;
				}
			}
			fine = stato;
		} else if (tipo_scelta==2){
		} else {
			Logger.log(Logger.ERROR, CLASS, METHOD, "Il tipo_scelta ha assunto un valore non conosciuto!");
		}
		return fine;
	}

}
